Predykcja
Jako zmienną celu obrano pojemnośc grawimetryczną.
//TODO delete
library("caret")
set.seed(23)
dataset_to_train <- select(data, -(Battery.ID:Formula.Discharge))
if (is.factor(data$Stability.Charge)) {
# Optional: If it should be numeric in the first place
data$Stability.Charge <- as.numeric(as.character(data$Stability.Charge))
}
head(dataset_to_train)
inTraining <-
createDataPartition(
y = data$Stability.Charge,
p = .75,
list = FALSE)
training <- dataset_to_train[ inTraining,]
testing <- dataset_to_train[-inTraining,]
ctrl <- trainControl(
# powtórzona ocena krzyżowa
method = "repeatedcv",
# liczba podziałów
number = 2,
# liczba powtórzeń
repeats = 5)
fit <- train(Stability.Charge ~ .,
data = training,
method = "rf",
trControl = ctrl,
# Paramter dla algorytmu uczącego
ntree = 10)
fit
Random Forest
3264 samples
11 predictor
No pre-processing
Resampling: Cross-Validated (2 fold, repeated 5 times)
Summary of sample sizes: 1633, 1631, 1632, 1632, 1632, 1632, ...
Resampling results across tuning parameters:
mtry RMSE Rsquared MAE
2 0.2446747 0.6656663 0.08814636
6 0.1820564 0.8036072 0.06937326
11 0.1834491 0.7999853 0.06828544
RMSE was used to select the optimal model using the smallest value.
The final value used for the model was mtry = 6.
idx <- createDataPartition(data$Gravimetric.Capacity,p=0.7, list=F)
d1 <- data.frame(gravimetricCapacity=data[idx,]$Gravimetric.Capacity)
d2 <- data.frame(gravimetricCapacity=data[-idx,]$Gravimetric.Capacity)
ggplot(mapping=aes(alpha=0.4)) +
geom_density(aes(gravimetricCapacity, fill="red"), d1) +
geom_density(aes(gravimetricCapacity, fill="blue"), d2) +
theme_minimal()

Przygotowanie zbioru do trenowania
Ze zbioru usunięto atrybuty:
- ID - nie ma wpływu na zmienną celu - Battery.Formula - bardzo dużo
unikalnych wartości, więcej niż połowa rozmiaru zbioru, istnieje ryzyko
przeuczenia i nadmiernej segmentacji - Formula.Charge - podobnie jak
Battery.Formula - bardzo dużo unikalnych wartości - Formula.Discharge -
podobnie jak wyżej
Skalowanie i normalizacja zmiennych
#dataset_train_selected_attr <- select(data, -c(Battery.ID, Battery.Formula, Formula.Charge, Formula.Discharge))
dataset_numerical_attrs <- select(data, numerical_attrs)
Ostrzeżenie: Using an external vector in selections was deprecated in tidyselect 1.1.0.
Please use `all_of()` or `any_of()` instead.
# Was:
data %>% select(numerical_attrs)
# Now:
data %>% select(all_of(numerical_attrs))
See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
preProc <- preProcess(dataset_numerical_attrs, method = c("center", "scale"))
data_scaled <- predict(preProc, newdata = dataset_numerical_attrs)
data_scaled$Working.Ion <- data$Working.Ion
data_scaled
skim(data_scaled)
── Data Summary ────────────────────────
Values
Name data_scaled
Number of rows 4351
Number of columns 13
_______________________
Column type frequency:
character 1
numeric 12
________________________
Group variables None
dummies <- dummyVars("~ .", data = data_scaled)
data_transformed <- data.frame(predict(dummies, newdata = data_scaled))
head(data_transformed)
anova_result <- aov(Gravimetric.Capacity ~ Battery.Formula, data = data)
summary(anova_result)
Df Sum Sq Mean Sq F value Pr(>F)
Battery.Formula 3300 118304814 35850 1.103e+26 <2e-16 ***
Residuals 1050 0 0
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Usunięcie zmiennych skorelowanych
# Usunięcie zmiennych skorelowanych
cor_matrix <- cor(data_transformed)
high_corr <- findCorrelation(cor_matrix, cutoff = 0.9)
data_reduced <- data_transformed[, -high_corr]
colnames(data_reduced[high_corr])
[1] "Volumetric.Energy"
targetAttr <- "Volumetric.Energy"
target <- data_reduced$Volumetric.Energy
predictors <- data_reduced[, -which(names(data_reduced) == targetAttr)]
data_reduced
# Podział danych
set.seed(123)
trainIndex <- createDataPartition(target, p = 0.7, list = FALSE)
trainData <- data_reduced[trainIndex, ]
testData <- data_reduced[-trainIndex, ]
ggplot() +
geom_density(data = trainData, aes(x = !!sym(targetAttr), fill = "Train"), alpha = 0.3) +
geom_density(data = testData, aes(x = !!sym(targetAttr), fill = "Test"), alpha = 0.3) +
scale_fill_manual(values = c("Train" = "red", "Test" = "blue")) +
theme_minimal() +
labs(title = "Porównanie rozkładu zmiennej celu w zbiorach treningowym i testowym",
x = targetAttr,
y = "Gęstość",
fill = "Zbiór")

targetAttr <- "Volumetric.Energy"
formula <- as.formula(paste(targetAttr, "~ ."))
model <- train(
formula,
data = trainData,
method = "lm",
trControl = trainControl(method = "cv", number = 10)
)
# Prognozy i ocena modelu
predictions <- predict(model, newdata = testData)
postResample(predictions, testData[[targetAttr]])
RMSE Rsquared MAE
0.6327532 0.6168179 0.4215428
# Wykres rzeczywistych vs przewidywanych wartości
plot(testData[[targetAttr]], predictions,
main = paste("Predicted vs Actual", targetAttr),
xlab = "Rzeczywiste", ylab = "Przewidywane")
abline(0, 1, col = "red")

LS0tDQp0aXRsZTogIlByb2dyYW1vd2FuaWUgdyBSOiBQcm9qZWt0Ig0Kc3VidGl0bGU6ICAgIkFuYWxpemEgYmF6eSBkYW55Y2ggbWF0ZXJpYcWCw7N3IHd5a29yenlzdHl3YW55Y2ggdyB0d29yemVuaXUgYmF0ZXJpaSINCmF1dGhvcjogS2xhdWRpYSBLb3dhbHNrYQ0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KDQojIFdzdMSZcA0KDQpgYGB7ciBnbG9iYWwgb3B0aW9ucywgaW5jbHVkZSA9IEZBTFNFfQ0KI2tuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvPUZBTFNFLCBpbmNsdWRlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UpDQpzZXQuc2VlZCgxMikNCmBgYA0KDQojIyBXeWtvcnp5c3RhbmUgYmlibGlvdGVraQ0KDQotICoqc2tpbXIqKiAtIFVtb8W8bGl3aWEgc3p5YmtpZSBpIHByemVqcnp5c3RlIGdlbmVyb3dhbmllIHN0YXR5c3R5ayBvcGlzb3d5Y2ggZGxhIHpiaW9yw7N3IGRhbnljaCwgZG9zdGFyY3phasSFYyBwb2RzdW1vd2FuaWEgZG9zdG9zb3dhbmUgZG8gcsOzxbxueWNoIHR5cMOzdyB6bWllbm55Y2guIA0KLSAqKmNvcnJwbG90KiogLSBTxYJ1xbx5IGRvIHdpenVhbGl6YWNqaSBtYWNpZXJ6eSBrb3JlbGFjamkuIA0KLSAqKkdHYWxseSoqIC0gUm96c3plcnphIG1vxbxsaXdvxZtjaSBnZ3Bsb3QyLCB1bW/FvGxpd2lhasSFYyB0d29yemVuaWUgemFhd2Fuc293YW55Y2ggd2l6dWFsaXphY2ppLCB0YWtpY2ggamFrIG1hY2llcnplIHBhciB3eWtyZXPDs3cgY3p5IGtvcmVsb2dyYW15LCBjbyBqZXN0IHByenlkYXRuZSB3IGFuYWxpemllIHdpZWxvd3ltaWFyb3dlai4gDQotICoqZHBseXIqKiAtIFphcGV3bmlhIHplc3RhdyBmdW5rY2ppIGRvIG1hbmlwdWxhY2ppIGRhbnltaSB3IHNwb3PDs2IgZGVrbGFyYXR5d255IGkgY3p5dGVsbnksIHVtb8W8bGl3aWFqxIVjIGZpbHRyb3dhbmllLCBzb3J0b3dhbmllLCBncnVwb3dhbmllIG9yYXogYWdyZWdvd2FuaWUgZGFueWNoIHcgcmFtYWNoIHrFgm/FvG9ueWNoIG9wZXJhY2ppLiANCi0gKipwbG90bHkqKiAtIFBvendhbGEgbmEgdHdvcnplbmllIGludGVyYWt0eXdueWNoIHd5a3Jlc8Ozdy4gDQotICoqY2FyZXQqKiAtIFXFgmF0d2lhIHByb2NlcyBidWRvd3kgbW9kZWxpIHByZWR5a2N5am55Y2gsIG9mZXJ1asSFYyBuYXJ6xJlkemlhIGRvIHByenlnb3Rvd2FuaWEgZGFueWNoLCBzZWxla2NqaSBjZWNoLCB0dW5pbmd1IGhpcGVycGFyYW1ldHLDs3cgb3JheiBvY2VueSBtb2RlbGkgdyBzcMOzam55IGkgemludGVncm93YW55IHNwb3PDs2IuDQoNCg0KYGBge3IgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KHNraW1yKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSgiSG1pc2MiKQ0KbGlicmFyeShtbGJlbmNoKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkocmxhbmcpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KUHLDs2JrYSB6YmlvcnUgZGFueWNoIG8gbWF0ZXJpYcWCYWNoIHd5a29yenlzdHl3YW55Y2ggdyBiYXRlcmlhY2guDQoNCmBgYHtyIGxvYWQgZGF0YSwgZWNobyA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsIHJlc3VsdHM9RkFMU0V9DQojdXJsZmlsZT0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0tsYXVkaWFLL0JhdHRlcnktbWF0ZXJpYWxzLWFuYWx5c2lzL3JlZnMvaGVhZHMvbWFzdGVyL21wX2JhdHRlcmllcy5jc3YiDQojZGF0YSA8LSByZWFkLmNzdih1cmwodXJsZmlsZSkpDQoNCmRhdGEgPC0gcmVhZC5jc3YoIkQ6XFxzdHVkaWFcXG1hZ2lzdGVya2FcXFpFRFxcUHJvamVrdF9SXFxtcF9iYXR0ZXJpZXMuY3N2IikNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmhlYWQoZGF0YSkNCmBgYA0KDQojIyBSb3ptaWFyIHpiaW9ydSBpIHBvZHN1bW93YW5pZSANCg0KWmJpw7NyIGRhbnljaCBtYSBgciBuY29sKGRhdGEpYCBhdHJ5YnV0w7N3IGkgYHIgbnJvdyhkYXRhKWAgcmVrb3Jkw7N3Lg0KDQpgYGB7ciBjb3VudF9uYV92YWx1ZXMsIGluY2x1ZGU9RkFMU0V9DQpzdW1fbmEgPC0gc3VtKGlzLm5hKGRhdGEpKQ0KYGBgDQoNClN1bWEgYnJha3VqxIVjeWNoIHdhcnRvxZtjaSB3IHpiaW9yemUgd3lub3NpIC4NCg0KIyMgQXRyeWJ1dHkNCg0KLSAqKkJhdHRlcnkgSUQqKjogSWRlbnR5ZmlrYXRvciBiYXRlcmlpLg0KLSAqKkJhdHRlcnkgRm9ybXVsYSoqOiBXesOzciBjaGVtaWN6bnkgbWF0ZXJpYcWCdSBiYXRlcmlpLg0KLSAqKldvcmtpbmcgSW9uKio6IEfFgsOzd255IGpvbiwga3TDs3J5IG9kcG93aWFkYSB6YSB0cmFuc3BvcnQgxYJhZHVua3UgdyBiYXRlcmlpLg0KLSAqKkZvcm11bGEgQ2hhcmdlKio6IFd6w7NyIGNoZW1pY3pueSBtYXRlcmlhxYJ1IGJhdGVyaWkgdyBzdGFuaWUgbmHFgmFkb3dhbnltLg0KLSAqKkZvcm11bGEgRGlzY2hhcmdlKio6IFd6w7NyIGNoZW1pY3pueSBtYXRlcmlhxYJ1IGJhdGVyaWkgdyBzdGFuaWUgcm96xYJhZG93YW55bS4NCi0gKipNYXggRGVsdGEgVm9sdW1lKio6IFptaWFuYSBvYmrEmXRvxZtjaSB3ICUgZGxhIGRhbmVnbyBrcm9rdSBuYXBpxJljaWEgemEgcG9tb2PEhSB3em9ydTogbWF4KGNoYXJnZSwgZGlzY2hhcmdlKS9taW4oY2hhcmdlLCBkaXNjaGFyZ2UpIC0xLg0KLSAqKkF2ZXJhZ2UgVm9sdGFnZSoqOiDFmnJlZG5pZSBuYXBpxJljaWUgZGxhIHBvc3pjemVnw7NsbmVnbyBrcm9rdSBuYXBpxJljaWEuDQotICoqR3JhdmltZXRyaWMgQ2FwYWNpdHkqKjogUG9qZW1ub8WbxIcgZ3Jhd2ltZXRyeWN6bmEsIGN6eWxpIGlsb8WbxIcgZW5lcmdpaSBuYSBqZWRub3N0a8SZIG1hc3kgKG1BaC9nKS4NCi0gKipWb2x1bWV0cmljIENhcGFjaXR5Kio6IFBvamVtbm/Fm8SHIHdvbHVtZXRyeWN6bmEsIGN6eWxpIGlsb8WbxIcgZW5lcmdpaSBuYSBqZWRub3N0a8SZIG9iasSZdG/Fm2NpIChtQWgvY23CsykuDQotICoqR3JhdmltZXRyaWMgRW5lcmd5Kio6IEfEmXN0b8WbxIcgZW5lcmdpaSB3IG9kbmllc2llbml1IGRvIG1hc3kgYmF0ZXJpaSAoV2gva2cpLg0KLSAqKlZvbHVtZXRyaWMgRW5lcmd5Kio6IEfEmXN0b8WbxIcgZW5lcmdpaSB3IG9kbmllc2llbml1IGRvIG9iasSZdG/Fm2NpIGJhdGVyaWkgKFdoL0wpLg0KLSAqKkF0b21pYyBGcmFjdGlvbiBDaGFyZ2UqKjogVWR6aWHFgiBhdG9tb3d5IHNrxYJhZG5pa8OzdyB3IHN0YW5pZSBuYcWCYWRvd2FueW0uDQotICoqQXRvbWljIEZyYWN0aW9uIERpc2NoYXJnZSoqOiBVZHppYcWCIGF0b21vd3kgc2vFgmFkbmlrw7N3IHcgc3RhbmllIHJvesWCYWRvd2FueW0uDQotICoqU3RhYmlsaXR5IENoYXJnZSoqOiBXc2thxbpuaWsgc3RhYmlsbm/Fm2NpIG1hdGVyaWHFgnUgdyBzdGFuaWUgbmHFgmFkb3dhbnltLg0KLSAqKlN0YWJpbGl0eSBEaXNjaGFyZ2UqKjogV3NrYcW6bmlrIHN0YWJpbG5vxZtjaSBtYXRlcmlhxYJ1IHcgc3RhbmllIHJvesWCYWRvd2FueW0uDQotICoqU3RlcHMqKjogTGljemJhIG9kcsSZYm55Y2gga3Jva8OzdyBuYXBpxJljaWEgb2QgcGXFgm5lZ28gbmHFgmFkb3dhbmlhIGRvIHJvesWCYWRvd2FuaWEsIG9wYXJ0YSBuYSBzdGFiaWxueWNoIHN0YW5hY2ggcG/Fm3JlZG5pY2guDQotICoqTWF4IFZvbHRhZ2UgU3RlcCoqOiBNYWtzeW1hbG5hIGJlend6Z2zEmWRuYSByw7PFvG5pY2EgbWnEmWR6eSBzxIVzaWVkbmltaSBrcm9rYW1pIG5hcGnEmWNpYS4NCg0KYGBge3IgcHJ0LCBlY2hvPUZBTFNFfQ0KcHJldHR5VGFibGUgPC0gZnVuY3Rpb24odGFibGVfZGYsIHJvdW5kX2RpZ2l0cz0yKSB7DQogICAgRFQ6OmRhdGF0YWJsZSh0YWJsZV9kZiwgc3R5bGU9ImJvb3RzdHJhcCIsIGZpbHRlciA9ICJ0b3AiLCByb3duYW1lcyA9IEZBTFNFLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJywgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpKSkgJT4lIGZvcm1hdFJvdW5kKG5hbWVzKGRwbHlyOjpzZWxlY3RfaWYodGFibGVfZGYsIGlzLm51bWVyaWMpKSwgcm91bmRfZGlnaXRzKQ0KfQ0KYGBgDQoNCiMjIyBLcsOzdGtpZSBwb2RzdW1vd2FuaWUgemJpb3J1DQoNCmBgYHtybSBlY2hvPUZBTFNFfQ0Kc2tpbV9zdW1tYXJ5IDwtIHNraW0oZGF0YSkNCg0KbnVtX2NoYXJhY3RlciA8LSBza2ltX3N1bW1hcnkgJT4lIGZpbHRlcihza2ltX3R5cGUgPT0gImNoYXJhY3RlciIpICU+JSBucm93KCkNCm51bV9udW1lcmljIDwtIHNraW1fc3VtbWFyeSAlPiUgZmlsdGVyKHNraW1fdHlwZSA9PSAibnVtZXJpYyIpICU+JSBucm93KCkNCm51bV9sb2dpY2FsIDwtIHNraW1fc3VtbWFyeSAlPiUgZmlsdGVyKHNraW1fdHlwZSA9PSAibG9naWNhbCIpICU+JSBucm93KCkNCmBgYA0KDQpaYmnDs3IgemF3aWVyYSAxMiBhdHJ5YnV0w7N3IG51bWVyeWN6bnljaCBpIDUgem5ha293eWNoIHcgdHltIDMga2F0ZWdvcnljem5lLg0KDQpUeXB5IGtvbHVtbjoNCi0gYHIgbnVtX2NoYXJhY3RlcmAga29sdW1ueSB6bmFrb3dlLA0KLSBgciBudW1fbnVtZXJpY2Aga29sdW1ueSBudW1lcnljem5lLA0KLSBgciBudW1fbG9naWNhbGAga29sdW1ueSBsb2dpY3puZSAoamXFm2xpIGlzdG5pZWrEhSkuDQoNCmBgYHtyfQ0KbnVtZXJpY2FsX2F0dHJzIDwtIGNvbG5hbWVzKHNlbGVjdChkYXRhLCAoTWF4LkRlbHRhLlZvbHVtZSA6IE1heC5Wb2x0YWdlLlN0ZXApKSkNCmBgYA0KDQpgYGB7cn0NCmRhdGEgJT4lDQogIHNlbGVjdChXb3JraW5nLklvbiwgTWF4LkRlbHRhLlZvbHVtZTpTdGFiaWxpdHkuRGlzY2hhcmdlKSAlPiUNCiAgZ3JvdXBfYnkoV29ya2luZy5Jb24pICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgbWVhbikpDQoNCmBgYA0KDQojIyMgQW5hbGl6YSBhdHJ5YnV0w7N3IGthdGVnb3J5Y3pueWNoDQoNCiMjIyMgUm96a8WCYWQgZ8WCw7N3bmV5Y2ggam9uw7N3DQoNCmBgYHtyIGVjaG8gPSBGQUxTRX0NCg0KZGF0YSAlPiUNCiAgZ3JvdXBfYnkoV29ya2luZy5Jb24pICU+JQ0KICBzdW1tYXJpc2UodG90YWxfaW9uID0gbigpKSAlPiUNCiAgZ2dwbG90KCkgKw0KICAgbGFicyh0aXRsZSA9IHBhc3RlKCJSb3prxYJhZCBnxYLDs3dueWNoIGpvbsOzdywga3TDs3JlIG9kcG93aWFkYWrEhSB6YSB0cmFuc3BvcnQgxYJhZHVua3UgdyBiYXRlcmlpIiksDQogICAgICAgICAgIHggPSAiV29ya2luZy5Jb24iLA0KICAgICAgICAgICB5ID0gIkNvdW50IikgKw0KICAgICAgZ2VvbV9iYXIoYWVzKHggPSByZW9yZGVyKFdvcmtpbmcuSW9uLCB0b3RhbF9pb24pLCB5ID0gdG90YWxfaW9uLCBmaWxsID0gV29ya2luZy5Jb24pLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICAgICAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQpOYSBwcnplZHN0YXdpb255bSB3eWtyZXNpZSB6YXByZXplbnRvd2FubyByb3prxYJhZCByw7PFvG55Y2ggZ8WCw7N3bnljaCBqb27Ds3cgdcW8eXdhbnljaCBkbyB0cmFuc3BvcnR1IMWCYWR1bmt1IHcgYmF0ZXJpYWNoLiBXeXJhxbpuaWUgZG9taW51amUgbGl0IChMaSksIGt0w7NyeSB3eXN0xJlwdWplIHpuYWN6bmllIGN6xJnFm2NpZWogbmnFvCBpbm5lIGpvbnkuIFN1Z2VydWplIHRvIHBvd3N6ZWNobmUgemFzdG9zb3dhbmllIHRlY2hub2xvZ2lpIG9wYXJ0eWNoIG5hIGxpdG93by1qb25vd3ljaCByb3p3acSFemFuaWFjaCwgY28gamVzdCB6Z29kbmUgeiBpY2ggc3plcm9raW0gd3lrb3J6eXN0YW5pZW0gdyBwcnplbXnFm2xlIGVsZWt0cm9uaWtpIGkgbWFnYXp5bm93YW5pYSBlbmVyZ2lpLiBQb3pvc3RhxYJlIGpvbnksIHRha2llIGphayB3YXDFhCAoQ2EpLCBtYWduZXogKE1nKSwgaSBjeW5rIChabiksIHLDs3duaWXFvCB6bmFqZHVqxIUgemFzdG9zb3dhbmllLCBhbGUgdyB6bmFjem5pZSBtbmllanN6eW0gemFrcmVzaWUuIE9iZWNub8WbxIcgam9uw7N3IHRha2ljaCBqYWsgc8OzZCAoTmEpIGkgcG90YXMgKEspIHdza2F6dWplIG5hIGJhZGFuaWEgbmFkIGFsdGVybmF0eXdhbWkgZGxhIGxpdHUsIGplZG5hayBpY2ggemFzdG9zb3dhbmllIGplc3Qgb2JlY25pZSBvZ3Jhbmljem9uZS4NCg0KDQojIyMjIFd6b3J5IGNoZW1pY3puZSBtYXRlcmlhxYJ1IGJhdGVyaWkNCg0KYGBge3IgZWNobyA9IEZBTFNFfQ0KcHJpbnQoY291bnQodW5pcXVlKGRhdGFbIkZvcm11bGEuQ2hhcmdlIl0pKSkNCg0KZm9ybXVsYV9jaGFyZ2Vfc3VtbWFyeSA8LSBkYXRhICU+JQ0KICBncm91cF9ieShGb3JtdWxhLkNoYXJnZSkgJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpDQoNCnBlcmNlbnRpbGVfOTkgPC0gcXVhbnRpbGUoc2VsZWN0KGZvcm11bGFfY2hhcmdlX3N1bW1hcnksIHRvdGFsKSwgcHJvYnMgPSAwLjk5LCBuYS5ybSA9IFRSVUUpDQpmb3JtdWxhX2NoYXJnZV92YWx1ZXNfOTlfcGVyY2VudGlsZSA8LSBmb3JtdWxhX2NoYXJnZV9zdW1tYXJ5ICU+JQ0KICBmaWx0ZXIodG90YWwgPj0gcGVyY2VudGlsZV85OSkNCg0KZm9ybXVsYV9jaGFyZ2VfdmFsdWVzXzk5X3BlcmNlbnRpbGUNCg0KZm9ybXVsYV9jaGFyZ2VfdmFsdWVzXzk5X3BlcmNlbnRpbGUgJT4lDQogIGdncGxvdCgpICsNCiAgIGxhYnModGl0bGUgPSBwYXN0ZSgiV3rDs3J5IGNoZW1pY3puZSBtYXRlcmlhxYJ1IGJhdGVyaWkgdyBzdGFuaWUgbmHFgmFkb3dhbnltIiksDQogICAgICAgICAgIHggPSAiRm9ybXVsYSBDaGFyZ2UiLA0KICAgICAgICAgICB5ID0gIkNvdW50IikgKw0KICAgICAgZ2VvbV9iYXIoYWVzKHggPSByZW9yZGVyKEZvcm11bGEuQ2hhcmdlLCB0b3RhbCksIHkgPSB0b3RhbCwgZmlsbCA9IEZvcm11bGEuQ2hhcmdlKSwgc3RhdCA9ICJpZGVudGl0eSIsICB3aWR0aCA9IDAuNSkgKw0KICAgICAgdGhlbWUoDQogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCBzaXplID0gOCkNCiAgICAgICkNCmBgYA0KVyB6YmlvcnplIGplc3QgMjA5NiByw7PFvG55Y2ggd3pvcsOzdyBjaGVtaWN6bnljaCBtYXRlcmlhxYLDs3cgYmF0ZXJpaS4gTmFqY3rEmcWbY2llaiB3eXN0xJlwdWrEhWN5bWkgd3pvcmFtaSBzxIUgTW5PMiwgVGlPMiwgVm8yLCBDck8yLCBOaU8yLCBGZU8yLg0KDQojIyMjIA0KDQoNCmBgYHtyfQ0KDQpjb250aW5vdXNfZGF0YSA8LSBzZWxlY3QoZGF0YSwgKE1heC5EZWx0YS5Wb2x1bWU6U3RhYmlsaXR5LkRpc2NoYXJnZSkpDQoNCmZvciAoYXR0cl9uYW1lIGluIG5hbWVzKGNvbnRpbm91c19kYXRhKSkgew0KICBwIDwtIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBhdHRyX25hbWUpKSArDQogICAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAic2t5Ymx1ZSIsIGFscGhhID0gMC41KSArDQogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJEZW5zaXR5IFBsb3Qgb2YiLCBhdHRyX25hbWUpLA0KICAgICAgICAgeCA9ICJNYXggRGVsdGEgVm9sdW1lIiwNCiAgICAgICAgIHkgPSAiRGVuc2l0eSIpICsNCiAgICB0aGVtZV9taW5pbWFsKCkNCiAgcHJpbnQocCkNCn0gICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgIA0KDQpgYGANCiMjIEtvcmVsYWNqYSB6bWllbm55Y2gNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpudW1iZXJfYXR0cl9kYXRhIDwtIHNlbGVjdChkYXRhLCAoTWF4LkRlbHRhLlZvbHVtZTpNYXguVm9sdGFnZS5TdGVwKSkNCg0KY29yX2RhdGEgPC0gY29yKG51bWJlcl9hdHRyX2RhdGEpDQoNCmNvcnJwbG90Lm1peGVkKGNvcl9kYXRhLA0KICB0bC5wb3MgPSAnbHQnLA0KICBudW1iZXIuY2V4ID0gMC41LA0KICBvcmRlciA9ICdBT0UnDQopDQpgYGANCg0KTmFqd3nFvHN6eSB3c3BvbGN6eW5uaWsga29vcmVsYWNqaSB3eXN0ZXB1amUgcG9taWVkenkgcGFyYW1pIGF0cnlidXTDs3c6XA0KLSBHcmF2aW1ldHJpYyBFbmVyZ3kgaSBWb2x1bWV0cmljIEVuZXJneSBcDQotIEdyYXZpbWV0cmljIENhcGNpdHkgaSBWb2x1bWV0cmljIENhcGFjaXR5IFwNCi0gU3RhYmlsaXR5IENoYXJnZSBpIFN0YWJpbGl0eSBEaXNjaGFyZ2UgXA0KLSBHcmF2aW1ldHJpYyBDYXBhY2l0eSBpIEF0b21pYyBGcmFjdGlvbiBEaXNjaGFyZ2UgXA0KLSBBdmVyYWdlIFZvbHRhZ2UgaSBHcmF2aW1ldHJpYyBFbmVyZ3kgDQoNCiMjIyBXeWtyZXMga29yZWxhY2ppIHBvbWnEmWR6eSBlbmVyZ2nEhSBncmF3aW1ldHJ5Y3puxIUgaSB3b2x1bWV0cnljem7EhQ0KDQpgYGB7cn0NCmNvcnJlbGF0aW9uIDwtIGNvcihkYXRhJEdyYXZpbWV0cmljLkVuZXJneSwgZGF0YSRWb2x1bWV0cmljLkVuZXJneSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IEdyYXZpbWV0cmljLkVuZXJneSwgeSA9IFZvbHVtZXRyaWMuRW5lcmd5KSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtvcmFuZ2UiLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiYmx1ZSIsIHNlID0gRkFMU0UsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJFbmVyZ3kgRGVuc2l0eSBSZWxhdGlvbnNoaXAiLA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JyZWxhdGlvbiwgMikpLA0KICAgICAgIHggPSAiR3JhdmltZXRyaWMgRW5lcmd5IERlbnNpdHkgKFdoL2tnKSIsDQogICAgICAgeSA9ICJWb2x1bWV0cmljIEVuZXJneSBEZW5zaXR5IChXaC9MKSIpICsNCiAgdGhlbWVfbGlnaHQoKQ0KYGBgDQpXeWtyZXMgcHJ6ZWRzdGF3aWEgKip6YWxlxbxub8WbxIcgbWnEmWR6eSBnxJlzdG/Fm2NpxIUgZW5lcmdpaSB3b2x1bWV0cnljem7EhSoqIChXaC9MLCBlbmVyZ2lhIG5hIGplZG5vc3RrxJkgb2JqxJl0b8WbY2kpICoqYSBncmF3aW1ldHJ5Y3puxIUqKiAoV2gva2csIGVuZXJnaWEgbmEgamVkbm9zdGvEmSBtYXN5KSwgZ2R6aWUgd2lkb2N6bmEgamVzdCAqKnNpbG5hIGtvcmVsYWNqYSoqIGRvZGF0bmlhIG1pxJlkenkgdHltaSBwYXJhbWV0cmFtaS4gR8SZc3RvxZvEhyBlbmVyZ2lpIGplc3QgKiprbHVjem93eW0gd3NrYcW6bmlraWVtIHd5ZGFqbm/Fm2NpIGJhdGVyaWkqKiAtIGltIHd5xbxzemEgd2FydG/Fm8SHLCB0eW0gd2nEmWNlaiBlbmVyZ2lpIG1vxbxlIGJ5xIcgem1hZ2F6eW5vd2FuZSB3IGRhbmVqIG9iasSZdG/Fm2NpIGx1YiBtYXNpZSBiYXRlcmlpLCBjbyBqZXN0IHN6Y3plZ8OzbG5pZSBpc3RvdG5lIHcgemFzdG9zb3dhbmlhY2ggbW9iaWxueWNoLCB0YWtpY2ggamFrIHBvamF6ZHkgZWxla3RyeWN6bmUgY3p5IHVyesSFZHplbmlhIHByemVub8WbbmUuICoqV2nEmWtzem/Fm8SHIGJhZGFueWNoIG1hdGVyaWHFgsOzdyBza3VwaWEgc2nEmSB3IHpha3Jlc2llIGRvIDIwMDAgV2gva2cgaSA3NTAwIFdoL0wqKiwgY2hvxIcgd3lzdMSZcHVqZSBraWxrYSBvYmllY3VqxIVjeWNoIHd5asSFdGvDs3cgbyB3ecW8c3p5Y2ggcGFyYW1ldHJhY2gsIGt0w7NyZSBtb2fEhSBzdGFub3dpxIcgcG90ZW5jamFsbmUga2llcnVua2kgcm96d29qdSBub3d5Y2gsIHd5ZGFqbmllanN6eWNoIGJhdGVyaWkuDQoNCg0KIyMjIFd5a3JlcyBrb3JlbGFjamkgcG9tacSZZHp5IHBvamVtbm/Fm2NpxIUgZ3Jhd2ltZXRyeWN6bsSFIGkgd29sdW1ldHJ5Y3puxIUNCg0KYGBge3J9DQpjb3JyZWxhdGlvbiA8LSBjb3IoZGF0YSRHcmF2aW1ldHJpYy5DYXBhY2l0eSwgZGF0YSRWb2x1bWV0cmljLkNhcGFjaXR5LCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gR3JhdmltZXRyaWMuQ2FwYWNpdHksIHkgPSBWb2x1bWV0cmljLkNhcGFjaXR5KSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtvcmFuZ2UiLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiYmx1ZSIsIHNlID0gRkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbnNoaXAgQmV0d2VlbiBHcmF2aW1ldHJpYyBhbmQgVm9sdW1ldHJpYyBDYXBhY2l0eSIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcnJlbGF0aW9uLCAyKSksDQogICAgICAgeCA9ICJHcmF2aW1ldHJpYyBDYXBhY2l0eSAobUFoL2cpIiwNCiAgICAgICB5ID0gIlZvbHVtZXRyaWMgQ2FwYWNpdHkgKG1BaC9jbcKzKSIpICsNCiAgdGhlbWVfbGlnaHQoKQ0KDQpgYGANCld5a3JlcyBwcnplZHN0YXdpYSAqKnphbGXFvG5vxZvEhyBtacSZZHp5IHBvamVtbm/Fm2NpxIUgd29sdW1ldHJ5Y3puxIUqKiAobUFoL2NtwrMsIGlsb8WbxIcgxYJhZHVua3UgbmEgamVkbm9zdGvEmSBvYmrEmXRvxZtjaSkgKiphIGdyYXdpbWV0cnljem7EhSoqIChtQWgvZywgaWxvxZvEhyDFgmFkdW5rdSBuYSBqZWRub3N0a8SZIG1hc3kpIG1hdGVyaWHFgsOzdywgZ2R6aWUgcG9qZW1ub8WbxIcgZ3Jhd2ltZXRyeWN6bmEgb2tyZcWbbGEgaWxlIGVuZXJnaWkgbW/FvG5hIHptYWdhenlub3dhxIcgdyBkYW5laiBtYXNpZSBtYXRlcmlhxYJ1LCBhIHdvbHVtZXRyeWN6bmEgLSBpbGUgdyBkYW5laiBvYmrEmXRvxZtjaSwgY28gbWEga2x1Y3pvd2Ugem5hY3plbmllIHByenkgcHJvamVrdG93YW5pdSBiYXRlcmlpIG8gcsOzxbxueW0gcHJ6ZXpuYWN6ZW5pdS4gKipXc3DDs8WCY3p5bm5payBrb3JlbGFjamkgMC44NiB3c2thenVqZSBuYSBzaWxuxIUgemFsZcW8bm/Fm8SHIG1pxJlkenkgdHltaSBwYXJhbWV0cmFtaSoqLCBjaG/EhyBuaWUgdGFrIHNpbG7EhSBqYWsgdyBwcnp5cGFka3UgZ8SZc3RvxZtjaSBlbmVyZ2lpLiBOYSBwcnp5a8WCYWQsIG1hdGVyaWHFgiBvIHd5c29raWVqIHBvamVtbm/Fm2NpIGdyYXdpbWV0cnljem5laiBtb8W8ZSBiecSHIGxla2tpLCBhbGUgemFqbW93YcSHIGR1xbxvIG1pZWpzY2EsIHBvZGN6YXMgZ2R5IG1hdGVyaWHFgiBvIHd5c29raWVqIHBvamVtbm/Fm2NpIHdvbHVtZXRyeWN6bmVqIG1vxbxlIGJ5xIcgY2nEmcW8c3p5LCBhbGUgYmFyZHppZWoga29tcGFrdG93eSAtIHd5YsOzciBtacSZZHp5IG5pbWkgemFsZcW8eSBvZCBrb25rcmV0bmVnbyB6YXN0b3Nvd2FuaWEgYmF0ZXJpaS4NCg0KIyMjIFd5a3JlcyBrb3JlbGFjamkgcG9tacSZZHp5IHN0YWJpbG5vxZtjacSFIG1hdGVyaWHFgnUgdyBzdGFuaWUgbmHFgmFkb3dhbnltIGkgcm96xYJhZG93YW55bQ0KDQpgYGB7cn0NCmNvcnJlbGF0aW9uIDwtIGNvcihkYXRhJFN0YWJpbGl0eS5DaGFyZ2UsIGRhdGEkU3RhYmlsaXR5LkRpc2NoYXJnZSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IFN0YWJpbGl0eS5DaGFyZ2UsIHkgPSBTdGFiaWxpdHkuRGlzY2hhcmdlKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJibHVlIiwgc2UgPSBGQUxTRSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlID0gIlN0YWJpbGl0eSBDaGFyZ2UgdnMgU3RhYmlsaXR5IERpc2NoYXJnZSIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcnJlbGF0aW9uLCAyKSksDQogICAgICAgeCA9ICJTdGFiaWxpdHkgQ2hhcmdlIiwNCiAgICAgICB5ID0gIlN0YWJpbGl0eSBEaXNjaGFyZ2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCld5a3JlcyBwcnplZHN0YXdpYSAqKnphbGXFvG5vxZvEhyBtacSZZHp5IHN0YWJpbG5vxZtjacSFIG1hdGVyaWHFgnUgdyBzdGFuaWUgbmHFgmFkb3dhbnltKiogKFN0YWJpbGl0eSBDaGFyZ2UpICoqYSBzdGFiaWxub8WbY2nEhSB3IHN0YW5pZSByb3rFgmFkb3dhbnltKiogKFN0YWJpbGl0eSBEaXNjaGFyZ2UpLCB6ZSB3c3DDs8WCY3p5bm5pa2llbSBrb3JlbGFjamkgMC44IHdza2F6dWrEhWN5bSBuYSBzaWxuxIUgZG9kYXRuacSFIHphbGXFvG5vxZvEhy4gU3RhYmlsbm/Fm8SHIG1hdGVyaWHFgnUgamVzdCAqKmtsdWN6b3d5bSBwYXJhbWV0cmVtIG9rcmXFm2xhasSFY3ltLCBqYWsgZG9icnplIG1hdGVyaWHFgiB6YWNob3d1amUgc3dvasSFIHN0cnVrdHVyxJkgaSB3xYJhxZtjaXdvxZtjaSBwb2RjemFzIGN5a2xpIMWCYWRvd2FuaWEgaSByb3rFgmFkb3dhbmlhKiogLSBpbSBuacW8c3phIHdhcnRvxZvEhywgdHltIG1hdGVyaWHFgiBqZXN0IGJhcmR6aWVqIHN0YWJpbG55IGkgYmV6cGllY3pueSB3IHXFvHl0a293YW5pdS4gV2nEmWtzem/Fm8SHIGJhZGFueWNoIG1hdGVyaWHFgsOzdyBza3VwaWEgc2nEmSB3IHpha3Jlc2llIG5pc2tpY2ggd2FydG/Fm2NpICgwLTIpIGRsYSBvYnUgcGFyYW1ldHLDs3csIGNvIGplc3QgcG/FvMSFZGFuZSwgbmF0b21pYXN0IHB1bmt0eSBvZHN0YWrEhWNlIG8gd3nFvHN6eWNoIHdhcnRvxZtjaWFjaCAocG93ecW8ZWogNCkgbW9nxIUgd3NrYXp5d2HEhyBuYSBtYXRlcmlhxYJ5IHByb2JsZW1hdHljem5lLCBrdMOzcmUgbW9nxIUgYnnEhyBtbmllaiBvZHBvd2llZG5pZSBkbyB6YXN0b3Nvd2HFhCB3IGJhdGVyaWFjaCB6ZSB3emdsxJlkdSBuYSBwb3RlbmNqYWxuxIUgbmllc3RhYmlsbm/Fm8SHLg0KDQojIyMgV3lrcmVzIGtvcmVsYWNqaSBwb21pxJlkenkgcG9qZW1ub8WbY2nEhSBncmF3aW1ldHJ5Y3puxIUgaSB1ZHppYcWCIGF0b21vd3ltIHNrxYJhZG5pa8OzdyB3IHN0YW5pZSByb3rFgmFkb3dhbnltDQoNCmBgYHtyfQ0KY29ycmVsYXRpb24gPC0gY29yKGRhdGEkR3JhdmltZXRyaWMuQ2FwYWNpdHksIGRhdGEkQXRvbWljLkZyYWN0aW9uLkRpc2NoYXJnZSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IEdyYXZpbWV0cmljLkNhcGFjaXR5LCB5ID0gQXRvbWljLkZyYWN0aW9uLkRpc2NoYXJnZSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBBdG9taWMuRnJhY3Rpb24uRGlzY2hhcmdlKSwgc2l6ZSA9IDMsIGFscGhhID0gMC43KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbG9yID0gInJlZCIsIHNlID0gVFJVRSwgZmlsbCA9ICJncmF5ODAiKSArDQogIGxhYnModGl0bGUgPSAiR3JhdmltZXRyaWMgQ2FwYWNpdHkgdnMgQXRvbWljIEZyYWN0aW9uIERpc2NoYXJnZSIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcnJlbGF0aW9uLCAyKSksDQogICAgICAgeCA9ICJHcmF2aW1ldHJpYyBDYXBhY2l0eSAobUFoL2cpIiwNCiAgICAgICB5ID0gIkF0b21pYyBGcmFjdGlvbiBEaXNjaGFyZ2UiKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJvcmFuZ2UiLCBoaWdoID0gInB1cnBsZSIpICsNCiAgdGhlbWVfbGlnaHQoKQ0KDQpgYGANCg0KV3lrcmVzIHByemVkc3Rhd2lhIHphbGXFvG5vxZvEhyBtacSZZHp5IHBvamVtbm/Fm2NpxIUgZ3Jhd2ltZXRyeWN6bsSFIChHcmF2aW1ldHJpYyBDYXBhY2l0eSwgbUFoL2cpIGEgdWR6aWHFgmVtIGF0b21vd3ltIHcgc3RhbmllIHJvesWCYWRvd2FuaWEgKEF0b21pYyBGcmFjdGlvbiBEaXNjaGFyZ2UpLiBNb8W8bmEgemFvYnNlcndvd2HEhyB1bWlhcmtvd2FuaWUgc2lsbsSFLCBkb2RhdG5pxIUgemFsZcW8bm/Fm8SHLCBjbyBwb3R3aWVyZHphIHdzcMOzxYJjenlubmlrIGtvcmVsYWNqaSB3eW5vc3rEhWN5IDAuNjguIFcgbWlhcsSZIHd6cm9zdHUgcG9qZW1ub8WbY2kgZ3Jhd2ltZXRyeWN6bmVqLCB1ZHppYcWCIGF0b21vd3kgdyBzdGFuaWUgcm96xYJhZG93YW5pYSB6d2nEmWtzemEgc2nEmSwgb3NpxIVnYWrEhWMgd2FydG/Fm8SHIG1ha3N5bWFsbsSFIGJsaXNrxIUgMS4wLg0KDQpLb2xvciBwdW5rdMOzdyByZXByZXplbnR1amUgd2FydG/Fm8SHIEF0b21pYyBGcmFjdGlvbiBEaXNjaGFyZ2UsIGdkemllIGphxZtuaWVqc3plIGtvbG9yeSB3c2thenVqxIUgbmEgbmnFvHN6ZSB3YXJ0b8WbY2ksIGEgY2llbW5pZWpzemUgbmEgd3nFvHN6ZS4gRGFuZSB3c2thenVqxIUsIMW8ZSB3acSZa3N6b8WbxIcgb2JzZXJ3YWNqaSB6bmFqZHVqZSBzacSZIHcgemFrcmVzaWUgbmlza2llaiBwb2plbW5vxZtjaSBncmF3aW1ldHJ5Y3puZWogKDwxMDAwIG1BaC9nKSwgYSBkbGEgd2FydG/Fm2NpIHBvd3nFvGVqIDIwMDAgbUFoL2cgemFsZcW8bm/Fm8SHIHN0YWplIHNpxJkgbmllbGluaW93YS4NCg0KU3VnZXJ1amUgdG8sIMW8ZSBtYXRlcmlhxYJ5IG8gd3nFvHN6ZWogcG9qZW1ub8WbY2kgZ3Jhd2ltZXRyeWN6bmVqIG1hasSFIHRlbmRlbmNqxJkgZG8gb3NpxIVnYW5pYSB3ecW8c3p5Y2ggdWR6aWHFgsOzdyBhdG9tb3d5Y2ggdyBzdGFuaWUgcm96xYJhZG93YW5pYS4NCg0KYGBge3J9DQp1bmlxdWUoZGF0YSRTdGVwcykNCg0KZmlsdGVyZWREYXRhIDwtIGRhdGEgJT4lZmlsdGVyKGRhdGEkU3RlcHMgPT0gMykNCmNvcnJlbGF0aW9uIDwtIGNvcihmaWx0ZXJlZERhdGEkQXZlcmFnZS5Wb2x0YWdlLCBmaWx0ZXJlZERhdGEkR3JhdmltZXRyaWMuRW5lcmd5LCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KZ2dwbG90KGZpbHRlcmVkRGF0YSwgYWVzKHggPSBBdmVyYWdlLlZvbHRhZ2UsIHkgPSBHcmF2aW1ldHJpYy5FbmVyZ3kpKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiZGFya2JsdWUiLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiZGFya3JlZCIsIHNlID0gRkFMU0UsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIFZvbHRhZ2UgdnMgR3JhdmltZXRyaWMgRW5lcmd5IiwNCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQoY29ycmVsYXRpb24sIDIpKSwNCiAgICAgICB4ID0gIkF2ZXJhZ2UgVm9sdGFnZSAoVikiLA0KICAgICAgIHkgPSAiR3JhdmltZXRyaWMgRW5lcmd5IERlbnNpdHkgKFdoL2tnKSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KYGBge3J9DQojIEdlbmVyb3dhbmllIHd5a3Jlc8OzdyBkbGEga2HFvGRlaiB3YXJ0b8WbY2kgU3RlcHMNCnAgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gQXZlcmFnZS5Wb2x0YWdlLCB5ID0gR3JhdmltZXRyaWMuRW5lcmd5KSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDMsIGFscGhhID0gMC43KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gImRhcmtyZWQiLCBzZSA9IEZBTFNFLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGZhY2V0X3dyYXAofiBTdGVwcywgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBWb2x0YWdlIHZzIEdyYXZpbWV0cmljIEVuZXJneSBmb3IgRWFjaCBTdGVwIiwNCiAgICAgICB4ID0gIkF2ZXJhZ2UgVm9sdGFnZSAoVikiLA0KICAgICAgIHkgPSAiR3JhdmltZXRyaWMgRW5lcmd5IERlbnNpdHkgKFdoL2tnKSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpnZ3Bsb3RseShwKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHBsb3RseSkNCg0KIyBPYmxpY3oga29yZWxhY2plIGRsYSBrYcW8ZGVqIGdydXB5DQpkYXRhX3dpdGhfY29yIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KFN0ZXBzKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGNvcnJlbGF0aW9uID0gY29yKEF2ZXJhZ2UuVm9sdGFnZSwgR3JhdmltZXRyaWMuRW5lcmd5LCB1c2UgPSAiY29tcGxldGUub2JzIiksDQogICAgbl9jYXNlcyA9IG4oKSAgIyBEb2RhaiBsaWN6YsSZIHByenlwYWRrw7N3DQogICkNCg0KIyBTdHfDs3J6IHd5a3Jlcw0KcCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBBdmVyYWdlLlZvbHRhZ2UsIHkgPSBHcmF2aW1ldHJpYy5FbmVyZ3kpKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiZGFya2JsdWUiLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiZGFya3JlZCIsIHNlID0gRkFMU0UsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZmFjZXRfd3JhcCh+IFN0ZXBzLCBzY2FsZXMgPSAiZnJlZSIsIA0KICAgICAgICAgICAgIGxhYmVsbGVyID0gbGFiZWxsZXIoU3RlcHMgPSBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAjIFpuYWpkxboga29yZWxhY2rEmSBkbGEgZGFuZWdvIGV0eWtpZXR5DQogICAgICAgICAgICAgICAgcm93IDwtIGRhdGFfd2l0aF9jb3JbZGF0YV93aXRoX2NvciRTdGVwcyA9PSB4LCBdDQogICAgICAgICAgICAgICAjIFNmb3JtYXR1aiBldHlraWV0xJkgeiBrb3JlbGFjasSFDQogICAgICAgICAgICAgIHBhc3RlMCh4LCAiIChDb3JyID0gIiwgcm91bmQocm93JGNvcnJlbGF0aW9uLCAyKSwgDQogICAgICAgICAgICAgICAgICAgICAgIiwgbiA9ICIsIHJvdyRuX2Nhc2VzLCAiKSIpDQogICAgICAgICAgICAgfSkpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJBdmVyYWdlIFZvbHRhZ2UgdnMgR3JhdmltZXRyaWMgRW5lcmd5IGZvciBFYWNoIFN0ZXAiLA0KICAgIHggPSAiQXZlcmFnZSBWb2x0YWdlIChWKSIsDQogICAgeSA9ICJHcmF2aW1ldHJpYyBFbmVyZ3kgRGVuc2l0eSAoV2gva2cpIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgS29ud2Vyc2phIGRvIHd5a3Jlc3UgaW50ZXJha3R5d25lZ28NCmdncGxvdGx5KHApDQpgYGANCg0KV3lrcmVzeSBwcnplZHN0YXdpYWrEhSB6YWxlxbxub8WbY2kgbWnEmWR6eSDFm3JlZG5pbSBuYXBpxJljaWVtIChBdmVyYWdlIFZvbHRhZ2UsIFYpIGEgZ8SZc3RvxZtjacSFIGVuZXJnaWkgZ3Jhd2ltZXRyeWN6bmVqIChHcmF2aW1ldHJpYyBFbmVyZ3kgRGVuc2l0eSwgV2gva2cpIGRsYSByw7PFvG55Y2ggd2FydG/Fm2NpIGtyb2t1IG5hcGnEmWNpYS4NCkRhbmUgd3NrYXp1asSFLCDFvGUgd3JheiB6ZSB3enJvc3RlbSDFm3JlZG5pZWdvIG5hcGnEmWNpYSB6d2nEmWtzemEgc2nEmSBnxJlzdG/Fm8SHIGVuZXJnaWkgZ3Jhd2ltZXRyeWN6bmVqLiBXacSZa3N6b8WbxIcgZGFueWNoIHNrdXBpYSBzacSZIHcgemFrcmVzaWUgbmlza2ljaCB3YXJ0b8WbY2kgbmFwacSZY2lhICg8MTAgViksIGNvIHN1Z2VydWplLCDFvGUgbWF0ZXJpYcWCeSBvIHd5xbxzenltIG5hcGnEmWNpdSBzxIUgbW5pZWogbGljem5lLCBhbGUgd3lrYXp1asSFIHdpxJlrc3rEhSBlZmVrdHl3bm/Fm8SHIGVuZXJnZXR5Y3puxIUuDQoNCiMjIFdpenVhbGl6YWNqYSBpbnRlcmFrdHl3bnljaCB3eWtyZXPDs3cgcHVkZcWCa293eWNoDQoNCg0KVyB0ZWogY3rEmcWbY2kgcmFwb3J0dSBwcnplZHN0YXdpb25vIGludGVyYWt0eXduZSB3eWtyZXN5IHB1ZGXFgmtvd2UgZGxhIGthxbxkZWogem1pZW5uZWogbnVtZXJ5Y3puZWogdyB6YmlvcnplIGRhbnljaC4gV3lrcmVzeSB0ZSB1bW/FvGxpd2lhasSFIGVrc3Bsb3JhY2rEmSByb3prxYJhZHUgd2FydG/Fm2NpLCBpZGVudHlmaWthY2rEmSBwb3RlbmNqYWxueWNoIHdhcnRvxZtjaSBvZHN0YWrEhWN5Y2ggb3JheiBwb3LDs3duYW5pZSB6bWllbm5vxZtjaSB3IGthxbxkZWogem1pZW5uZWouDQoNCmBgYHtyIGludGVyYWN0aXZlX2JveHBsb3RzfQ0KZm9yIChhdHRyX25hbWUgaW4gbmFtZXMobnVtYmVyX2F0dHJfZGF0YSkpIHsNCiAgcCA8LSBwbG90X2x5KA0KICAgIG51bWJlcl9hdHRyX2RhdGEsIA0KICAgIHkgPSB+Z2V0KGF0dHJfbmFtZSksDQogICAgdHlwZSA9ICJib3giLA0KICAgIGJveHBvaW50cyA9ICJhbGwiLA0KICAgIGppdHRlciA9IDAuMywNCiAgICBwb2ludHBvcyA9IC0xLjgNCiAgKSAlPiUNCiAgICBsYXlvdXQoDQogICAgICB0aXRsZSA9IHBhc3RlKCJCb3hwbG90IG9mIiwgYXR0cl9uYW1lKSwNCiAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9IGF0dHJfbmFtZSkNCiAgICApDQoNCiAgcHJpbnQocCkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ3JvdXBfYnkoQmF0dGVyeS5Gb3JtdWxhKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkNCmBgYA0KDQpgYGB7cn0NCmRhdGEgJT4lDQogIGdyb3VwX2J5KEJhdHRlcnkuRm9ybXVsYSkgJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpDQpgYGANCg0KDQojIyBBbmFsaXphIHfFgmHFm2Npd2/Fm2NpDQoNCiMjIyBTdGFiaWxub8WbxIcgdyBzdGFuaWUgbmHFgmFkb3dhbnltIGkgcm96xYJhZG93YW55bQ0KDQoqKlN0YWJpbGl0eSBDaGFyZ2UqKg0KDQotIE9rcmXFm2xhIHN0YWJpbG5vxZvEhyBtYXRlcmlhxYJ1IHcgYmF0ZXJpaSwgZ2R5IGplc3QgdyBwZcWCbmkgbmHFgmFkb3dhbnkNCi0gV3nFvHN6YSBzdGFiaWxub8WbxIcgb3puYWN6YSwgxbxlIG1hdGVyaWHFgiBixJlkemllIG1uaWVqIHBvZGF0bnkgbmEgdXN6a29kemVuaWEgbHViIGRlZ3JhZGFjasSZIHBvZGN6YXMgxYJhZG93YW5pYS4gSmVzdCB0byBpc3RvdG5lLCBhYnkgemFwZXduacSHIGTFgnVnb3Ryd2HFgmUgZHppYcWCYW5pZSBiYXRlcmlpIGJleiB1dHJhdHkgamVqIHfFgmHFm2Npd2/Fm2NpLg0KDQoqKlN0YWJpbGl0eSBEaXNjaGFyZ2UqKg0KDQotIE9rcmXFm2xhIHN0YWJpbG5vxZvEhyBtYXRlcmlhxYJ1IHcgYmF0ZXJpaSwgZ2R5IGplc3QgdyBwZcWCbmkgcm96xYJhZG93YW55Lg0KLSBTdGFiaWxub8WbxIcgdyBzdGFuaWUgcm96xYJhZG93YW55bSBqZXN0IGtsdWN6b3dhIGRsYSB1dHJ6eW1hbmlhIGVmZWt0eXdub8WbY2kgYmF0ZXJpaSBwcnpleiB3aWVsZSBjeWtsaSDFgmFkb3dhbmlhIGkgcm96xYJhZG93YW5pYS4gWmFwZXduaWEgb25hLCDFvGUgbWF0ZXJpYcWCIG5pZSB1bGVnbmllIGRlZ3JhZGFjamksIGNvIG1vZ8WCb2J5IHByb3dhZHppxIcgZG8gem1uaWVqc3plbmlhIHBvamVtbm/Fm2NpIGkgxbx5d290bm/Fm2NpIGJhdGVyaWkuDQoNCg0KYGBge3J9DQpzdW1tYXJ5X3N0YWJpbGl0eV9jaGFyZ2Vfd29ya2luZ19pb24gPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoV29ya2luZy5Jb24pICU+JQ0KICBzdW1tYXJpc2Uoc3RhYmlsaXR5X2NoYXJnZV9tZWFuID0gbWVhbihTdGFiaWxpdHkuQ2hhcmdlKSwNCiAgICAgICAgICAgIHN0YWJpbGl0eV9jaGFyZ2VfbWVkaWFuID0gbWVkaWFuKFN0YWJpbGl0eS5DaGFyZ2UpLA0KICAgICAgICAgICAgc3RhYmlsaXR5X2NoYXJnZV9taW4gPSBtaW4oU3RhYmlsaXR5LkNoYXJnZSksDQogICAgICAgICAgICBzdGFiaWxpdHlfY2hhcmdlX21heCA9IG1heChTdGFiaWxpdHkuQ2hhcmdlKSwNCiAgICAgICAgICAgIHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHN0YWJpbGl0eV9jaGFyZ2VfbWVkaWFuKSkNCg0Kc3VtbWFyeV9zdGFiaWxpdHlfY2hhcmdlX3dvcmtpbmdfaW9uDQoNCmdncGxvdChzdW1tYXJ5X3N0YWJpbGl0eV9jaGFyZ2Vfd29ya2luZ19pb24sIGFlcyh4ID0gdG90YWwsIHkgPSBzdGFiaWxpdHlfY2hhcmdlX21lZGlhbikpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDMpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJSZWxhdGlvbiBiZXR3ZWVuIFRvdGFsIGFuZCBTdGFiaWxpdHkgQ2hhcmdlIE1lYW4iLA0KICAgIHggPSAiVG90YWwiLA0KICAgIHkgPSAiU3RhYmlsaXR5IENoYXJnZSBNZWFuIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCnBsb3RfbHkoc3VtbWFyeV9zdGFiaWxpdHlfY2hhcmdlX3dvcmtpbmdfaW9uLCB4ID0gfnRvdGFsLCB5ID0gfnN0YWJpbGl0eV9jaGFyZ2VfbWVkaWFuLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLCB0ZXh0ID0gfnBhc3RlKFdvcmtpbmcuSW9uKSkNCg0Kc3VtbWFyeV9zdGFiaWxpdHlfZGlzY2hhcmdlX3dvcmtpbmdfaW9uIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KFdvcmtpbmcuSW9uKSAlPiUNCiAgc3VtbWFyaXNlKHN0YWJpbGl0eV9kaXNjaGFyZ2VfbWVhbiA9IG1lYW4oU3RhYmlsaXR5LkRpc2NoYXJnZSksDQogICAgICAgICAgICBzdGFiaWxpdHlfZGlzY2hhcmdlX21lZGlhbiA9IG1lZGlhbihTdGFiaWxpdHkuRGlzY2hhcmdlKSwNCiAgICAgICAgICAgIHN0YWJpbGl0eV9kaXNjaGFyZ2VfbWluID0gbWluKFN0YWJpbGl0eS5EaXNjaGFyZ2UpLA0KICAgICAgICAgICAgc3RhYmlsaXR5X2Rpc2NoYXJnZV9tYXggPSBtYXgoU3RhYmlsaXR5LkRpc2NoYXJnZSksDQogICAgICAgICAgICB0b3RhbCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhzdGFiaWxpdHlfZGlzY2hhcmdlX21lZGlhbikpDQoNCnN1bW1hcnlfc3RhYmlsaXR5X2Rpc2NoYXJnZV93b3JraW5nX2lvbg0KDQpnZ3Bsb3Qoc3VtbWFyeV9zdGFiaWxpdHlfZGlzY2hhcmdlX3dvcmtpbmdfaW9uLCBhZXMoeCA9IHRvdGFsLCB5ID0gc3RhYmlsaXR5X2Rpc2NoYXJnZV9tZWRpYW4sIGxhYmVsID0gV29ya2luZy5Jb24pKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAzKSArDQogIGdlb21fbGFiZWwoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUmVsYXRpb24gYmV0d2VlbiBUb3RhbCBhbmQgU3RhYmlsaXR5IERpc2NoYXJnZSBNZWFuIiwNCiAgICB4ID0gIlRvdGFsIiwNCiAgICB5ID0gIlN0YWJpbGl0eSBEaXNjaGFyZ2UgTWVhbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwbG90X2x5KHN1bW1hcnlfc3RhYmlsaXR5X2Rpc2NoYXJnZV93b3JraW5nX2lvbiwgeCA9IH50b3RhbCwgeSA9IH5zdGFiaWxpdHlfZGlzY2hhcmdlX21lZGlhbiwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJywgdGV4dCA9IH5wYXN0ZShXb3JraW5nLklvbikpDQpgYGANCk1hdGVyaWHFgnksIGt0w7NyZSB3eWthenVqxIUgbmFqd3nFvHN6xIUgc3RhYmlsbm/Fm8SHIHcgc3RhbmllIG5hxYJhZG93YW5tIHRvIFksIEFsLCBaTiwgQ2EsIE1nLCBhIHcgc3RhbmllIHJvesWCYWRvd2FueW0gWSwgTWcsIFpuLCBBbC4NCg0KDQpgYGB7cn0NCnN1bW1hcnlfbWF4X2RhdGFfdm9sdW1lX3dvcmtpbmdfaW9uIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KFdvcmtpbmcuSW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1heF9kYXRhX3ZvbHVtZV9tZWFuID0gbWVhbihNYXguRGVsdGEuVm9sdW1lKSwNCiAgICAgICAgICAgIG1heF9kYXRhX3ZvbHVtZV9tZWRpYW4gPSBtZWRpYW4oTWF4LkRlbHRhLlZvbHVtZSksDQogICAgICAgICAgICBtYXhfZGF0YV92b2x1bWVfbWluID0gbWluKE1heC5EZWx0YS5Wb2x1bWUpLA0KICAgICAgICAgICAgbWF4X2RhdGFfdm9sdW1lX21heCA9IG1heChNYXguRGVsdGEuVm9sdW1lKSwNCiAgICAgICAgICAgIHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShtYXhfZGF0YV92b2x1bWVfbWVhbikNCg0Kc3VtbWFyeV9tYXhfZGF0YV92b2x1bWVfd29ya2luZ19pb24NCg0KcGxvdF9seShzdW1tYXJ5X21heF9kYXRhX3ZvbHVtZV93b3JraW5nX2lvbiwgeCA9IH50b3RhbCwgeSA9IH5tYXhfZGF0YV92b2x1bWVfbWVkaWFuLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLCB0ZXh0ID0gfnBhc3RlKFdvcmtpbmcuSW9uKSkNCmBgYA0KDQoNCg0KIyMjIFBvamVtbm/Fm8SHIGkgZ8SZc3RvxZvEhyBncmF3aW1ldHJ5Y3puYQ0KDQoqKlBvamVtbm/Fm8SHIEdyYXdpbWV0cnljem5hKioNCg0KLSBNaWVyenksIGlsZSBlbmVyZ2lpIGVsZWt0cnljem5laiBiYXRlcmlhIG1vxbxlIHByemVjaG93YcSHIHcgcHJ6ZWxpY3plbml1IG5hIGplZG5vc3RrxJkgbWFzeSAobUFoL2cpLg0KLSBJbSB3ecW8c3phIHBvamVtbm/Fm8SHIGdyYXdpbWV0cnljem5hLCB0eW0gd2nEmWNlaiBlbmVyZ2lpIGJhdGVyaWEgbW/FvGUgcHJ6ZWNob3d5d2HEhyBwcnp5IHRlaiBzYW1laiBtYXNpZS4gSmVzdCB0byBpc3RvdG5lIGRsYSB1cnrEhWR6ZcWEIHByemVub8WbbnljaCwgZ2R6aWUgemFsZcW8eSBuYW0gbmEgbWFrc3ltYWxpemFjamkgZW5lcmdpaSBwcnp5IG5pZXdpZWxraWVqIG1hc2llLg0KDQoqKkfEmXN0b8WbxIcgR3Jhd2ltZXRyeWN6bmEqKg0KDQotIE96bmFjemEsIGlsZSBlbmVyZ2lpIGRvc3TEmXBuZWogamVzdCB3IGJhdGVyaWkgdyBvZG5pZXNpZW5pdSBkbyBqZWogbWFzeSAoV2gva2cpLg0KLSBHxJlzdG/Fm8SHIGdyYXdpbWV0cnljem5hIGluZm9ybXVqZSwgamFrIGVmZWt0eXduaWUgYmF0ZXJpYSBtYWdhenludWplIGVuZXJnacSZIHcgc3Rvc3Vua3UgZG8gc3dvamVqIHdhZ2kuIEplc3Qga2x1Y3pvd2EgZGxhIGFwbGlrYWNqaSwgZ2R6aWUgd2HFvG5hIGplc3QgemFyw7N3bm8gcG9qZW1ub8WbxIcgZW5lcmdldHljem5hLCBqYWsgaSBsZWtrb8WbxIcsIG5wLiB3IHNhbW9jaG9kYWNoIGVsZWt0cnljem55Y2ggY3p5IGRyb25hY2guDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Kc3VtbWFyeV9ncmF2aW1ldHJpY19lbmVyZ3lfd29ya2luZ19pb24gPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoV29ya2luZy5Jb24pICU+JQ0KICBzdW1tYXJpc2UoZ3JhdmltZXRyaWNfZW5lcmd5X21lYW4gPSBtZWFuKEdyYXZpbWV0cmljLkVuZXJneSksDQogICAgICAgICAgICBncmF2aW1ldHJpY19lbmVyZ3lfbWVkaWFuID0gbWVkaWFuKEdyYXZpbWV0cmljLkVuZXJneSksLA0KICAgICAgICAgICAgZ3JhdmltZXRyaWNfZW5lcmd5X21pbiA9IG1pbihHcmF2aW1ldHJpYy5FbmVyZ3kpLA0KICAgICAgICAgICAgZ3JhdmltZXRyaWNfZW5lcmd5X21heCA9IG1heChHcmF2aW1ldHJpYy5FbmVyZ3kpLA0KICAgICAgICAgICAgdG90YWwgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoZ3JhdmltZXRyaWNfZW5lcmd5X21lZGlhbikpDQoNCnN1bW1hcnlfZ3JhdmltZXRyaWNfZW5lcmd5X3dvcmtpbmdfaW9uDQoNCnBsb3RfbHkoc3VtbWFyeV9ncmF2aW1ldHJpY19lbmVyZ3lfd29ya2luZ19pb24sIHggPSB+dG90YWwsIHkgPSB+Z3JhdmltZXRyaWNfZW5lcmd5X21lZGlhbiwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJywgdGV4dCA9IH5wYXN0ZShXb3JraW5nLklvbikpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAiR3JhdmltZXRyaWMgZW5lcmd5IG1lZGlhbiBmb3IgV29ya2luZyBJb24iKQ0KDQoNCnN1bW1hcnlfZ3JhdmltZXRyaWNfY2FwYWNpdHlfd29ya2luZ19pb24gPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoV29ya2luZy5Jb24pICU+JQ0KICBzdW1tYXJpc2UoZ3JhdmltZXRyaWNfY2FwYWNpdHlfbWVhbiA9IG1lYW4oR3JhdmltZXRyaWMuQ2FwYWNpdHkpLA0KICAgICAgICAgICAgZ3JhdmltZXRyaWNfY2FwYWNpdHlfbWVkaWFuID0gbWVkaWFuKEdyYXZpbWV0cmljLkNhcGFjaXR5KSwNCiAgICAgICAgICAgIGdyYXZpbWV0cmljX2NhcGFjaXR5X21pbiA9IG1pbihHcmF2aW1ldHJpYy5DYXBhY2l0eSksDQogICAgICAgICAgICBncmF2aW1ldHJpY19jYXBhY2l0eV9tYXggPSBtYXgoR3JhdmltZXRyaWMuQ2FwYWNpdHkpLA0KICAgICAgICAgICAgdG90YWwgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoZ3JhdmltZXRyaWNfY2FwYWNpdHlfbWVkaWFuKSkNCg0Kc3VtbWFyeV9ncmF2aW1ldHJpY19jYXBhY2l0eV93b3JraW5nX2lvbg0KDQpwbG90X2x5KHN1bW1hcnlfZ3JhdmltZXRyaWNfY2FwYWNpdHlfd29ya2luZ19pb24sIHggPSB+dG90YWwsIHkgPSB+Z3JhdmltZXRyaWNfY2FwYWNpdHlfbWVkaWFuLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLCB0ZXh0ID0gfnBhc3RlKFdvcmtpbmcuSW9uKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICJHcmF2aW1ldHJpYyBjYXBhY2l0eSBtZWRpYW4gZm9yIFdvcmtpbmcgSW9uIikNCg0KYGBgDQoNCk5handpxJlrc3rEhSBwb2plbW5vxZtjacSFIGdyYXdpbWV0cnljem7EhSBjaGFyYWtldGVyeXp1asSFIHNpxJkgbWF0ZXJpYcWCeSBBbCwgWSwgTWcsIENhLCBabiwgYSBnxJlzdG/Fm2NpxIUgZ3Jhd2ltZXRyeWN6bsSFIENhLCBMaSwgWSwgTmEsIE1nLg0KDQoNCmBgYHtyfQ0KDQoNCmZvcm11bGFfY2hhcmdlX3N1bW1hcnkgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoRm9ybXVsYS5DaGFyZ2UpICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2ModG90YWwpKQ0KDQpwZXJjZW50aWxlXzk5IDwtIHF1YW50aWxlKHNlbGVjdChmb3JtdWxhX2NoYXJnZV9zdW1tYXJ5LCB0b3RhbCksIHByb2JzID0gMC45OSwgbmEucm0gPSBUUlVFKQ0KZm9ybXVsYV9jaGFyZ2VfdmFsdWVzXzk5X3BlcmNlbnRpbGUgPC0gZm9ybXVsYV9jaGFyZ2Vfc3VtbWFyeSAlPiUNCiAgZmlsdGVyKHRvdGFsID49IHBlcmNlbnRpbGVfOTkpDQoNCmZvcm11bGFfY2hhcmdlX3ZhbHVlc185OV9wZXJjZW50aWxlDQoNCg0Kc3VtbWFyeV9kYXRhIDwtIGRhdGEgJT4lDQogIGZpbHRlcihGb3JtdWxhLkNoYXJnZSAlaW4lIGZvcm11bGFfY2hhcmdlX3ZhbHVlc185OV9wZXJjZW50aWxlJEZvcm11bGEuQ2hhcmdlKSAlPiUNCiAgZ3JvdXBfYnkoRm9ybXVsYS5DaGFyZ2UpICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCksDQogICAgc3RhYmlsaXR5X2NoYXJnZV9tZWFuID0gbWVhbihTdGFiaWxpdHkuQ2hhcmdlKSwNCiAgICAgICAgICAgIHN0YWJpbGl0eV9jaGFyZ2VfbWVkaWFuID0gbWVkaWFuKFN0YWJpbGl0eS5DaGFyZ2UpLA0KICAgICAgICAgICAgc3RhYmlsaXR5X2NoYXJnZV9taW4gPSBtaW4oU3RhYmlsaXR5LkNoYXJnZSksDQogICAgICAgICAgICBzdGFiaWxpdHlfY2hhcmdlX21heCA9IG1heChTdGFiaWxpdHkuQ2hhcmdlKSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpDQoNCg0KZ2dwbG90KHN1bW1hcnlfZGF0YSwgYWVzKHggPSB0b3RhbCwgeSA9IHN0YWJpbGl0eV9jaGFyZ2VfbWVhbikpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDMpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKyAgIyBPcHRpb25hbCB0cmVuZCBsaW5lDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUmVsYXRpb24gYmV0d2VlbiBUb3RhbCBhbmQgU3RhYmlsaXR5IENoYXJnZSBNZWFuIiwNCiAgICB4ID0gIlRvdGFsIiwNCiAgICB5ID0gIlN0YWJpbGl0eSBDaGFyZ2UgTWVhbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQojIFBlcmZvcm0gQU5PVkENCiNhbm92YV9yZXN1bHQgPC0gYW92KE1heC5EZWx0YS5Wb2x1bWUgfiBCYXR0ZXJ5LkZvcm11bGEsIGRhdGEgPSBkYXRhKQ0KI3N1bW1hcnkoYW5vdmFfcmVzdWx0KQ0KDQphbm92YV9yZXN1bHQgPC0gYW92KFZvbHVtZXRyaWMuRW5lcmd5IH4gQmF0dGVyeS5Gb3JtdWxhLCBkYXRhID0gZGF0YSkNCnN1bW1hcnkoYW5vdmFfcmVzdWx0KQ0KYGBgDQoNCiMgUHJlZHlrY2phDQoNCkpha28gem1pZW5uxIUgY2VsdSBvYnJhbm8gcG9qZW1ub8WbYyBncmF3aW1ldHJ5Y3puxIUuIA0KDQovL1RPRE8gZGVsZXRlDQpgYGB7cn0NCmxpYnJhcnkoImNhcmV0IikNCnNldC5zZWVkKDIzKQ0KDQpkYXRhc2V0X3RvX3RyYWluICA8LSBzZWxlY3QoZGF0YSwgLShCYXR0ZXJ5LklEOkZvcm11bGEuRGlzY2hhcmdlKSkNCg0KaWYgKGlzLmZhY3RvcihkYXRhJFN0YWJpbGl0eS5DaGFyZ2UpKSB7DQogICAgIyBPcHRpb25hbDogSWYgaXQgc2hvdWxkIGJlIG51bWVyaWMgaW4gdGhlIGZpcnN0IHBsYWNlDQogICAgZGF0YSRTdGFiaWxpdHkuQ2hhcmdlIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGEkU3RhYmlsaXR5LkNoYXJnZSkpDQp9DQoNCg0KaGVhZChkYXRhc2V0X3RvX3RyYWluKQ0KDQppblRyYWluaW5nIDwtIA0KICAgIGNyZWF0ZURhdGFQYXJ0aXRpb24oDQogICAgICAgIHkgPSBkYXRhJFN0YWJpbGl0eS5DaGFyZ2UsDQogICAgICAgIHAgPSAuNzUsDQogICAgICAgIGxpc3QgPSBGQUxTRSkNCg0KdHJhaW5pbmcgPC0gZGF0YXNldF90b190cmFpblsgaW5UcmFpbmluZyxdDQp0ZXN0aW5nICA8LSBkYXRhc2V0X3RvX3RyYWluWy1pblRyYWluaW5nLF0NCg0KY3RybCA8LSB0cmFpbkNvbnRyb2woDQogICAgIyBwb3d0w7Nyem9uYSBvY2VuYSBrcnp5xbxvd2ENCiAgICBtZXRob2QgPSAicmVwZWF0ZWRjdiIsDQogICAgIyBsaWN6YmEgcG9kemlhxYLDs3cNCiAgICBudW1iZXIgPSAyLA0KICAgICMgbGljemJhIHBvd3TDs3J6ZcWEDQogICAgcmVwZWF0cyA9IDUpDQoNCmZpdCA8LSB0cmFpbihTdGFiaWxpdHkuQ2hhcmdlIH4gLiwNCiAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmcsDQogICAgICAgICAgICAgbWV0aG9kID0gInJmIiwNCiAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsLA0KICAgICAgICAgICAgICMgUGFyYW10ZXIgZGxhIGFsZ29yeXRtdSB1Y3rEhWNlZ28NCiAgICAgICAgICAgICBudHJlZSA9IDEwKQ0KDQpmaXQNCmBgYA0KYGBge3J9DQppZHggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRhJEdyYXZpbWV0cmljLkNhcGFjaXR5LHA9MC43LCBsaXN0PUYpDQpkMSA8LSBkYXRhLmZyYW1lKGdyYXZpbWV0cmljQ2FwYWNpdHk9ZGF0YVtpZHgsXSRHcmF2aW1ldHJpYy5DYXBhY2l0eSkNCmQyIDwtIGRhdGEuZnJhbWUoZ3JhdmltZXRyaWNDYXBhY2l0eT1kYXRhWy1pZHgsXSRHcmF2aW1ldHJpYy5DYXBhY2l0eSkNCg0KZ2dwbG90KG1hcHBpbmc9YWVzKGFscGhhPTAuNCkpICsgDQogZ2VvbV9kZW5zaXR5KGFlcyhncmF2aW1ldHJpY0NhcGFjaXR5LCBmaWxsPSJyZWQiKSwgZDEpICsgDQogZ2VvbV9kZW5zaXR5KGFlcyhncmF2aW1ldHJpY0NhcGFjaXR5LCBmaWxsPSJibHVlIiksIGQyKSArIA0KIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClByenlnb3Rvd2FuaWUgemJpb3J1IGRvIHRyZW5vd2FuaWENCg0KWmUgemJpb3J1IHVzdW5pxJl0byBhdHJ5YnV0eTogXA0KLSBJRCAtIG5pZSBtYSB3cMWCeXd1IG5hIHptaWVubsSFIGNlbHUNCi0gQmF0dGVyeS5Gb3JtdWxhIC0gYmFyZHpvIGR1xbxvIHVuaWthbG55Y2ggd2FydG/Fm2NpLCB3acSZY2VqIG5pxbwgcG/Fgm93YSByb3ptaWFydSB6YmlvcnUsIGlzdG5pZWplIHJ5enlrbyBwcnpldWN6ZW5pYSBpIG5hZG1pZXJuZWogc2VnbWVudGFjamkNCi0gRm9ybXVsYS5DaGFyZ2UgLSBwb2RvYm5pZSBqYWsgIEJhdHRlcnkuRm9ybXVsYSAtIGJhcmR6byBkdcW8byB1bmlrYWxueWNoIHdhcnRvxZtjaQ0KLSBGb3JtdWxhLkRpc2NoYXJnZSAtIHBvZG9ibmllIGphayB3ecW8ZWoNCg0KU2thbG93YW5pZSBpIG5vcm1hbGl6YWNqYSB6bWllbm55Y2gNCg0KYGBge3J9DQojZGF0YXNldF90cmFpbl9zZWxlY3RlZF9hdHRyICA8LSBzZWxlY3QoZGF0YSwgLWMoQmF0dGVyeS5JRCwgQmF0dGVyeS5Gb3JtdWxhLCBGb3JtdWxhLkNoYXJnZSwgRm9ybXVsYS5EaXNjaGFyZ2UpKQ0KZGF0YXNldF9udW1lcmljYWxfYXR0cnMgPC0gc2VsZWN0KGRhdGEsIG51bWVyaWNhbF9hdHRycykNCnByZVByb2MgPC0gcHJlUHJvY2VzcyhkYXRhc2V0X251bWVyaWNhbF9hdHRycywgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQpkYXRhX3NjYWxlZCA8LSBwcmVkaWN0KHByZVByb2MsIG5ld2RhdGEgPSBkYXRhc2V0X251bWVyaWNhbF9hdHRycykNCmRhdGFfc2NhbGVkJFdvcmtpbmcuSW9uIDwtIGRhdGEkV29ya2luZy5Jb24NCmRhdGFfc2NhbGVkDQpgYGANCg0KDQpgYGB7cn0NCnNraW0oZGF0YV9zY2FsZWQpDQpgYGANCg0KDQpgYGB7cn0NCmR1bW1pZXMgPC0gZHVtbXlWYXJzKCJ+IC4iLCBkYXRhID0gZGF0YV9zY2FsZWQpDQpkYXRhX3RyYW5zZm9ybWVkIDwtIGRhdGEuZnJhbWUocHJlZGljdChkdW1taWVzLCBuZXdkYXRhID0gZGF0YV9zY2FsZWQpKQ0KDQpoZWFkKGRhdGFfdHJhbnNmb3JtZWQpDQpgYGANCg0KYGBge3J9DQphbm92YV9yZXN1bHQgPC0gYW92KEdyYXZpbWV0cmljLkNhcGFjaXR5IH4gQmF0dGVyeS5Gb3JtdWxhLCBkYXRhID0gZGF0YSkNCnN1bW1hcnkoYW5vdmFfcmVzdWx0KQ0KYGBgDQoNClVzdW5pxJljaWUgem1pZW5ueWNoIHNrb3JlbG93YW55Y2gNCg0KYGBge3J9DQojIFVzdW5pxJljaWUgem1pZW5ueWNoIHNrb3JlbG93YW55Y2gNCmNvcl9tYXRyaXggPC0gY29yKGRhdGFfdHJhbnNmb3JtZWQpDQpoaWdoX2NvcnIgPC0gZmluZENvcnJlbGF0aW9uKGNvcl9tYXRyaXgsIGN1dG9mZiA9IDAuOSkNCmRhdGFfcmVkdWNlZCA8LSBkYXRhX3RyYW5zZm9ybWVkWywgLWhpZ2hfY29ycl0NCmNvbG5hbWVzKGRhdGFfcmVkdWNlZFtoaWdoX2NvcnJdKQ0KYGBgDQoNCmBgYHtyfQ0KDQp0YXJnZXRBdHRyIDwtICJWb2x1bWV0cmljLkVuZXJneSINCnRhcmdldCA8LSBkYXRhX3JlZHVjZWQkVm9sdW1ldHJpYy5FbmVyZ3kNCnByZWRpY3RvcnMgPC0gZGF0YV9yZWR1Y2VkWywgLXdoaWNoKG5hbWVzKGRhdGFfcmVkdWNlZCkgPT0gdGFyZ2V0QXR0cildDQpgYGANCg0KYGBge3J9DQpkYXRhX3JlZHVjZWQNCmBgYA0KDQpgYGB7cn0NCiMgUG9kemlhxYIgZGFueWNoDQpzZXQuc2VlZCgxMjMpDQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24odGFyZ2V0LCBwID0gMC43LCBsaXN0ID0gRkFMU0UpDQp0cmFpbkRhdGEgPC0gZGF0YV9yZWR1Y2VkW3RyYWluSW5kZXgsIF0NCnRlc3REYXRhIDwtIGRhdGFfcmVkdWNlZFstdHJhaW5JbmRleCwgXQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IHRyYWluRGF0YSwgYWVzKHggPSAhIXN5bSh0YXJnZXRBdHRyKSwgZmlsbCA9ICJUcmFpbiIpLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IHRlc3REYXRhLCBhZXMoeCA9ICEhc3ltKHRhcmdldEF0dHIpLCBmaWxsID0gIlRlc3QiKSwgYWxwaGEgPSAwLjMpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiVHJhaW4iID0gInJlZCIsICJUZXN0IiA9ICJibHVlIikpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJQb3LDs3duYW5pZSByb3prxYJhZHUgem1pZW5uZWogY2VsdSB3IHpiaW9yYWNoIHRyZW5pbmdvd3ltIGkgdGVzdG93eW0iLA0KICAgICAgIHggPSB0YXJnZXRBdHRyLA0KICAgICAgIHkgPSAiR8SZc3RvxZvEhyIsDQogICAgICAgZmlsbCA9ICJaYmnDs3IiKQ0KYGBgDQoNCg0KYGBge3J9DQp0YXJnZXRBdHRyIDwtICJWb2x1bWV0cmljLkVuZXJneSINCmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSh0YXJnZXRBdHRyLCAifiAuIikpDQoNCm1vZGVsIDwtIHRyYWluKA0KICBmb3JtdWxhLCANCiAgZGF0YSA9IHRyYWluRGF0YSwgDQogIG1ldGhvZCA9ICJsbSIsIA0KICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApDQopDQpgYGANCg0KDQpgYGB7cn0NCiMgUHJvZ25venkgaSBvY2VuYSBtb2RlbHUNCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YSkNCnBvc3RSZXNhbXBsZShwcmVkaWN0aW9ucywgdGVzdERhdGFbW3RhcmdldEF0dHJdXSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBXeWtyZXMgcnplY3p5d2lzdHljaCB2cyBwcnpld2lkeXdhbnljaCB3YXJ0b8WbY2kNCnBsb3QodGVzdERhdGFbW3RhcmdldEF0dHJdXSwgcHJlZGljdGlvbnMsIA0KICAgICBtYWluID0gcGFzdGUoIlByZWRpY3RlZCB2cyBBY3R1YWwiLCB0YXJnZXRBdHRyKSwNCiAgICAgeGxhYiA9ICJSemVjenl3aXN0ZSIsIHlsYWIgPSAiUHJ6ZXdpZHl3YW5lIikNCmFibGluZSgwLCAxLCBjb2wgPSAicmVkIikNCg0KYGBgDQoNCg==